use crate::components::{number_label::NumberLabel, slider::Slider};
use shared::{clone, DelayConfig, JitterConfig, LossConfig, NetemProfile};
use std::{cell::RefCell, ops::Deref, rc::Rc};
use ybc::{Checkbox, Control, Field, Input};
use yew::prelude::*;

#[derive(Clone, Properties, PartialEq)]
pub struct EditProfileProps {
    #[prop_or(false)]
    pub disabled: bool,
    pub profile: NetemProfile,
    pub callbacks: Rc<RefCell<Option<EditProfileCallbacks>>>,
    #[prop_or_default]
    pub on_accepted: Callback<()>,
}

#[derive(Clone, PartialEq)]
pub struct EditProfileCallbacks {
    pub get_editing: Callback<(), NetemProfile>,
}

#[function_component]
pub fn EditProfile(props: &EditProfileProps) -> Html {
    let name_state = use_state(|| props.profile.name.clone());
    let delay_state = use_state(|| props.profile.delay());
    let loss_state = use_state(|| props.profile.loss());
    let ppp_state = use_state(|| props.profile.is_poisson_loss());
    let jitter_state = use_state(|| props.profile.jitter());
    let burst_state = use_state(|| props.profile.burst().0);
    let gap_state = use_state(|| props.profile.burst().1);

    let get_editing = Callback::from(clone!(
        [
            name_state,
            delay_state,
            loss_state,
            ppp_state,
            jitter_state,
            burst_state,
            gap_state
        ],
        move |()| -> NetemProfile {
            let mut profile = NetemProfile::default();
            profile.name = name_state.deref().clone();

            let delay = *delay_state;
            if delay > 0 {
                profile.cfgs.push(DelayConfig::Value(delay).into());
            }

            let loss = *loss_state;
            if loss > 0.0 {
                if *ppp_state {
                    profile.cfgs.push(LossConfig::Poisson(loss).into());
                } else {
                    profile.cfgs.push(LossConfig::Stable(loss).into());
                }
            }

            let jitter = *jitter_state;
            if jitter > 0 {
                profile.cfgs.push(JitterConfig::Stable(jitter).into());
            }

            let burst = i16::min(*burst_state, 2000);
            let gap = i32::min(*gap_state, 60_000);
            if burst > 0 && gap > 0 {
                profile.cfgs.push(
                    JitterConfig::Burst {
                        burst,
                        interval: gap,
                    }
                    .into(),
                );
            }

            profile
        }
    ));

    props.callbacks.borrow_mut().replace(EditProfileCallbacks {
        get_editing: get_editing.clone(),
    });

    let disabled = props.disabled;
    html! {
        <div class="form">
            <DelayField {disabled} delay={delay_state} />
            <LossField {disabled} loss={loss_state} ppp={ppp_state} />
            <JitterField {disabled} jitter={jitter_state} />
            <BurstField {disabled} burst={burst_state} gap={gap_state} />
        </div>
    }
}

#[derive(Properties, PartialEq)]
struct NameFieldProps {
    name: UseStateHandle<String>,
}

#[function_component]
fn NameField(props: &NameFieldProps) -> Html {
    let name = use_state(|| props.name.deref().clone());
    let on_name_update = {
        let name = name.clone();
        let props_name = props.name.clone();
        move |value: String| {
            name.set(value.clone());
            props_name.set(value);
        }
    };

    html! {<>
        <Field classes={"is-horizontal"} label={"名称"} horizontal={true}>
            <Field>
                <Control>
                    <Input name={"delay"} value={ format!("{}", *name) } update={on_name_update}/>
                </Control>
            </Field>
        </Field>
    </>}
}

#[derive(Properties, PartialEq)]
struct DelayFieldProps {
    #[prop_or(false)]
    disabled: bool,
    delay: UseStateHandle<i16>,
}

#[function_component]
fn DelayField(props: &DelayFieldProps) -> Html {
    let delay = props.delay.clone();
    let on_delay_update = clone!([delay], move |value| {
        delay.set(value as i16);
    });

    html! {<div class="field">
        <NumberLabel title={"延迟"} text={format!("{}", *delay)} postfix={"ms"} />
        <Slider disabled={props.disabled} max={500} value={*delay as isize} oninput={on_delay_update} />
    </div>}
}

#[derive(Properties, PartialEq)]
struct LossFieldProps {
    #[prop_or(false)]
    disabled: bool,
    loss: UseStateHandle<f32>,
    ppp: UseStateHandle<bool>,
}

#[function_component]
fn LossField(props: &LossFieldProps) -> Html {
    let loss = props.loss.clone();
    let on_loss_percent_update = clone!([loss], move |value| {
        loss.set(value as f32 / 100.0);
    });

    let ppp = props.ppp.clone();
    let on_ppp_update = clone!([ppp], move |checked| {
        ppp.set(checked);
    });

    html! {<div class="field">
        <NumberLabel title={"丢包率"} text={format!("{:.0}", *loss * 100.0)} postfix={"%"} />
        <Slider disabled={props.disabled} min={0} max={80} value={(*loss * 100.0) as isize} oninput={on_loss_percent_update} />
        <div>
            <Checkbox disabled={props.disabled} name={"ppp"} checked={*ppp} update={on_ppp_update}>{ "非均匀丢包" }</Checkbox>
        </div>
    </div>}
}

#[derive(Properties, PartialEq)]
struct JitterFieldProps {
    #[prop_or(false)]
    disabled: bool,
    jitter: UseStateHandle<i16>,
}

#[function_component]
fn JitterField(props: &JitterFieldProps) -> Html {
    let jitter = props.jitter.clone();
    let on_jitter_update = clone!([jitter], move |value| {
        jitter.set(value as i16);
    });

    html! {<div class="field">
        <NumberLabel title={"抖动"} text={format!("{}", *jitter)} postfix={"ms"} />
        <Slider disabled={props.disabled} min={0} max={500} value={*jitter as isize} oninput={on_jitter_update} />
    </div>}
}

#[derive(Properties, PartialEq)]
struct BurstFieldProps {
    #[prop_or(false)]
    disabled: bool,
    burst: UseStateHandle<i16>,
    gap: UseStateHandle<i32>,
}

#[function_component]
fn BurstField(props: &BurstFieldProps) -> Html {
    let burst = props.burst.clone();
    let on_burst_update = clone!([burst], move |value| {
        burst.set(value as i16);
    });

    // let gap = props.gap.clone();
    // let on_gap_update = clone!([gap], move |value| {
    //     gap.set(value as i16);
    // });

    html! {<div class="field" style="width:152px">
        <NumberLabel title={"突发"} text={format!("{}", *burst)} postfix={"ms"} />
        <Slider disabled={props.disabled} min={0} max={2000} value={*burst as isize} oninput={on_burst_update} />
    </div>}
}
